package com.gjy.service.uuid.algorithm;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 雪花算法ID生成器
 * 生成64位长整型ID，包含时间戳、机器ID、实例ID和序列号
 * <p>
 * ID结构：
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 -
 * 000000000000
 * 1位符号位 + 41位时间戳 + 5位系统编码 + 5位机器编码 + 12位序列号
 *
 * @author gjy
 * @version 1.0
 * @since 2025-10-13 09:55:38
 */
public class SnowflakeIdGenerator {

    private static final Logger logger = LoggerFactory.getLogger(SnowflakeIdGenerator.class);

    // 起始时间戳 (2020-01-01 00:00:00)
    private static final long START_TIMESTAMP = 1577836800000L;

    // 各部分位数
    private static final long SEQUENCE_BIT = 12; // 序列号占用位数
    private static final long MACHINE_BIT = 5; // 机器编码占用位数
    private static final long SYSTEM_BIT = 5; // 系统编码占用位数

    // 各部分最大值
    private static final long MAX_SYSTEM_NUM = ~(-1L << SYSTEM_BIT);
    private static final long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    // 各部分左移位数
    private static final long MACHINE_LEFT = SEQUENCE_BIT;
    private static final long SYSTEM_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private static final long TIMESTAMP_LEFT = SYSTEM_LEFT + SYSTEM_BIT;

    private final long systemId; // 系统编码 (0-31)
    private final long machineId; // 机器编码 (0-31)

    private long sequence = 0L; // 序列号
    private long lastTimestamp = -1L; // 上次生成ID的时间戳

    /**
     * 构造函数
     *
     * @param systemId  系统编码 (0-31)
     * @param machineId 机器编码 (0-31)
     */
    public SnowflakeIdGenerator(long systemId, long machineId) {
        if (systemId > MAX_SYSTEM_NUM || systemId < 0) {
            throw new IllegalArgumentException("系统编码必须在0-" + MAX_SYSTEM_NUM + "之间");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("机器编码必须在0-" + MAX_MACHINE_NUM + "之间");
        }

        this.systemId = systemId;
        this.machineId = machineId;

        logger.info("雪花算法ID生成器初始化 - 系统编码: {}, 机器编码: {}", systemId, machineId);
    }

    /**
     * 生成下一个ID
     *
     * @return 生成的ID
     */
    public synchronized long nextId() {
        long currentTimestamp = getCurrentTimestamp();

        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨，拒绝生成ID");
        }

        if (currentTimestamp == lastTimestamp) {
            // 同一毫秒内，序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                // 序列号溢出，等待下一毫秒
                currentTimestamp = getNextMill();
            }
        } else {
            // 不同毫秒，序列号重置
            sequence = 0L;
        }

        lastTimestamp = currentTimestamp;

        // 组装ID
        return (currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT // 时间戳部分
                | systemId << SYSTEM_LEFT // 系统编码部分
                | machineId << MACHINE_LEFT // 机器编码部分
                | sequence; // 序列号部分
    }

    /**
     * 获取当前时间戳
     *
     * @return 当前时间戳
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 获取下一毫秒时间戳
     *
     * @return 下一毫秒时间戳
     */
    private long getNextMill() {
        long mill = getCurrentTimestamp();
        while (mill <= lastTimestamp) {
            mill = getCurrentTimestamp();
        }
        return mill;
    }

    /**
     * 解析ID，获取各个组成部分
     *
     * @param id 要解析的ID
     * @return ID组件信息
     */
    public IdComponents parseId(long id) {
        long timestamp = (id >> TIMESTAMP_LEFT) + START_TIMESTAMP;
        long systemId = (id >> SYSTEM_LEFT) & MAX_SYSTEM_NUM;
        long machineId = (id >> MACHINE_LEFT) & MAX_MACHINE_NUM;
        long sequence = id & MAX_SEQUENCE;

        return new IdComponents(timestamp, systemId, machineId, sequence);
    }

    /**
     * ID组件信息
     */
    public static class IdComponents {
        private final long timestamp;
        private final long systemId;
        private final long machineId;
        private final long sequence;

        public IdComponents(long timestamp, long systemId, long machineId, long sequence) {
            this.timestamp = timestamp;
            this.systemId = systemId;
            this.machineId = machineId;
            this.sequence = sequence;
        }

        public long getTimestamp() {
            return timestamp;
        }

        public long getSystemId() {
            return systemId;
        }

        public long getMachineId() {
            return machineId;
        }

        public long getSequence() {
            return sequence;
        }

        @Override
        public String toString() {
            return String.format("ID组件[时间戳=%d, 系统编码=%d, 机器编码=%d, 序列号=%d]",
                    timestamp, systemId, machineId, sequence);
        }
    }

}
